package kpi.javatraining6.gnatiuk.src.tester;


import kpi.javatraining6.gnatiuk.src.tester.readers.ConfigDOMReader;
import kpi.javatraining6.gnatiuk.src.tester.user.User;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger {
    private static Logger logger;
    private String userActionLog;
    private String exceptionsLog;
    private Document userLogDoc;
    private Document exceptionLogDoc;
    private XPath xpath;
    private boolean active;


    private Logger (){
        userActionLog = ConfigDOMReader.getInstance().getUserActionLog();
        exceptionsLog = ConfigDOMReader.getInstance().getExceptionsLog();
        active = ConfigDOMReader.getInstance().isLoggerActive();
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = domFactory.newDocumentBuilder();
            userLogDoc = builder.parse(userActionLog);
            exceptionLogDoc = builder.parse(exceptionsLog);
        } catch (ParserConfigurationException |SAXException| IOException e) {
            System.out.println("cant create DOM for logger");
            System.exit(1);
        }
        XPathFactory factory = XPathFactory.newInstance();
        xpath = factory.newXPath();
    }

    public static Logger getInstance() {
        if (logger == null) {
            logger = new Logger();
        }
        return logger;
    }

    public void recordUserAction(String userLogin, String message) {
        if (!active) {
            return;
        }
        String xPathExpression = String.format("//user[@name='%s']", userLogin);
        try {
            XPathExpression expr = xpath.compile(xPathExpression);
            Node user = (Node)expr.evaluate(userLogDoc, XPathConstants.NODE);
            if (user == null) {
                user = recordNewUser(userLogin);
            }
            String actionRecord = generateActonRecord(message);
            recordActionIntoDOM(user, actionRecord);
            saveFile(userLogDoc, userActionLog);
        } catch (XPathExpressionException e) {
            logger.recordExceptionalEvent(e);
            System.out.println("cant record action");
            System.exit(1);
        }
    }

    private String generateActonRecord(String message) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = new Date();
        return String.format("%s %s", dateFormat.format(date), message);
    }

    public void recordExceptionalEvent(Exception exception) {
        if (!active) {
            return;
        }
        String eventRecord = generateExceptionRecord(exception);
        Element event = exceptionLogDoc.createElement("event");
        Node eventMessage = exceptionLogDoc.createTextNode(eventRecord);
        event.appendChild(eventMessage);
        exceptionLogDoc.getDocumentElement().appendChild(event);
        saveFile(exceptionLogDoc, exceptionsLog);
    }

    private void saveFile(Document doc, String fileName) {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        try {
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(new File(fileName));
            transformer.transform(source, result);
        } catch (TransformerException e) {
            System.out.println("file not saved");
            System.exit(1);
        }
    }

    private void recordActionIntoDOM(Node user, String message) {
         Element action = userLogDoc.createElement("action");
         Node actionMessage = userLogDoc.createTextNode(message);
         action.appendChild(actionMessage);
         user.appendChild(action);
    }

    private String generateExceptionRecord(Exception e) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = new Date();
        return String.format("%s %s in %s", dateFormat.format(date), e.toString(), e.getStackTrace()[0]);
    }

    private Node recordNewUser(String userLogin) {
        Element user = userLogDoc.createElement("user");
        user.setAttribute("name", userLogin);
        userLogDoc.getDocumentElement().appendChild(user);
        return user;
    }

    //just for test
    public void deleteUser(String userName) {
        String xPathExpression = String.format("//user[@name='%s']", userName);
        try {
            XPathExpression expr = xpath.compile(xPathExpression);
            Node user = (Node)expr.evaluate(userLogDoc, XPathConstants.NODE);
        } catch (XPathExpressionException e) {
            logger.recordExceptionalEvent(e);
            System.exit(1);
        }

    }
}
